/*
-----------------------------------------------------------------------------
Filename:    TutorialApplication.cpp
-----------------------------------------------------------------------------

This source file is part of the
   ___                 __    __ _ _    _ 
  /___\__ _ _ __ ___  / / /\ \ (_) | _(_)
 //  // _` | '__/ _ \ \ \/  \/ / | |/ / |
/ \_// (_| | | |  __/  \  /\  /| |   <| |
\___/ \__, |_|  \___|   \/  \/ |_|_|\_\_|
      |___/                              
      Tutorial Framework
      http://www.ogre3d.org/tikiwiki/
-----------------------------------------------------------------------------
Basic Tutorial 2         Cameras, Lights, and Shadows
See http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Basic+Tutorial+2&structure=Tutorials
*/
#include "BasicTutorial2.h"
#include <iostream>

//-------------------------------------------------------------------------------------
BasicTutorial2::BasicTutorial2(void)
{
}
//-------------------------------------------------------------------------------------
BasicTutorial2::~BasicTutorial2(void)
{
}

//-------------------------------------------------------------------------------------
void BasicTutorial2::createCamera(void)
{
	// create the camera
    mCamera = mSceneMgr->createCamera("PlayerCam");

	// set camera position and direction: we will be placing objects around the origin, 
	// so we'll put the Camera a good distance in the +z direction and have the Camera face the origin.
    mCamera->setPosition(Ogre::Vector3(0,10,500));
		// lookAt() computes the yaw, rotate, and pitch needed to look at that point
		// also available por scene nodes
    mCamera->lookAt(Ogre::Vector3(0,0,0));
	// set the near clip distance: when you "go through" a close object, i.e., when you stop rendering
	// something too close. Similarly, the far clip distance is used to stop rendering objects too
	// far away, e.g. to increase the rendering ratio
    mCamera->setNearClipDistance(5);
	// mCamera->setFarClipDistance(500); // freak effect with this particular scene
		// needed for the call to setFarClipDistance() (for reasons unknown)
	// mSceneMgr->setShadowUseInfiniteFarPlane(false);

	// The OgreBites namespace doesn't appear in the API reference http://www.ogre3d.org/docs/api/html/files.html.
	// It seems to be part of the Ogre samples: http://www.ogre3d.org/tikiwiki/tiki-index.php?page=OgreBites
	mCameraMan = new OgreBites::SdkCameraMan(mCamera);   // create a default camera controller
}
 
//-------------------------------------------------------------------------------------
/**
- You must tell the RenderWindow which Cameras to display on the screen, and what portion of the window to render it in. 
The area in which you tell the RenderWindow to display the Camera is your Viewport.
- Under most typical uses of Ogre (no split screens or stuff), you will generally create only one Camera, register the Camera 
to use the entire RenderWindow, and thus only have one Viewport object.
*/
void BasicTutorial2::createViewports(void)
{
	//  To create the Viewport for the entire window we simply call the addViewport function of RenderWindow and 
	// supply it with the Camera we are using
    Ogre::Viewport* vp = mWindow->addViewport(mCamera);
	// black background (in RGB)
	vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
	// Alter the camera aspect ratio to match the viewport: adjust to actual screen and not fullscreen render
    mCamera->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
}

//-------------------------------------------------------------------------------------
void BasicTutorial2::createScene(void)
{
	// lights and shadows
		// WARNING: no ambient light (black light) implies that without another light
		// source all we will see is a black screen
	mSceneMgr->setAmbientLight(Ogre::ColourValue(0, 0, 0));
		// we use the most "expensive" kind of shadows
	mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);

		// now we add a point light (emits light in every direction equally) so we can see something
	Ogre::Light* pointLight = mSceneMgr->createLight("pointLight");
    pointLight->setType(Ogre::Light::LT_POINT);
    pointLight->setPosition(Ogre::Vector3(0, 150, 250));
			// change diffuse and specular colors to red
	pointLight->setDiffuseColour(1.0, 0.0, 0.0);
    pointLight->setSpecularColour(1.0, 0.0, 0.0)
		;
		// Let's also add a small amount of yellow directional light that is shining towards the 
		// front of the ninja below
	Ogre::Light* directionalLight = mSceneMgr->createLight("directionalLight");
    directionalLight->setType(Ogre::Light::LT_DIRECTIONAL);
			// since directional light is supposed to come from a far-off distance, 
			// we do not have to set its position, only its direction.
	directionalLight->setDirection(Ogre::Vector3( 0, -1, 1 ));
    directionalLight->setDiffuseColour(Ogre::ColourValue(.25, .25, 0));
    directionalLight->setSpecularColour(Ogre::ColourValue(.25, .25, 0));

		// We will now create a blue spotlight (like a flashlight)
	Ogre::Light* spotLight = mSceneMgr->createLight("spotLight");
    spotLight->setType(Ogre::Light::LT_SPOTLIGHT);
			// spotlights have both position and direction, as one would expect
	spotLight->setPosition(Ogre::Vector3(300, 300, 0));
	spotLight->setDirection(-1, -1, 0);
			// Modelling a flashlight beam, spotlights use an inner and outer circe for 
			// the width of the beam
	spotLight->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(50));
    spotLight->setDiffuseColour(0, 0, 1.0);
    spotLight->setSpecularColour(0, 0, 1.0);

	// some entity to see the shadows on it
	Ogre::Entity* entNinja = mSceneMgr->createEntity("Ninja", "ninja.mesh");
	entNinja->setCastShadows(true);
	Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	node->attachObject(entNinja);


	// something for the entity to stand on, and then cast shadows onto
		// a plane with the positive  y axis as its normal (i.e. face up) and no distance from the origin
	Ogre::Plane plane(Ogre::Vector3::UNIT_Y, 0);
		// use MeshManager to create a mesh from our plane so we can use it
		// we have registered our plane to be 1500 by 1500 in size and the generated mesh is called "ground"
	Ogre::MeshManager::getSingleton().createPlane("ground", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
		 plane, 1500, 1500, 20, 20, true, 1, 5, 5, Ogre::Vector3::UNIT_Z);
		// then we create an entity with the "ground" mesh, and attach it to a node in the child scene
	Ogre::Entity* entGround = mSceneMgr->createEntity("GroundEntity", "ground");
	node->attachObject(entGround);
		// avoid the ground to cast shadows: we can to cast shadows onto t
	entGround->setCastShadows(false);
		// add a predefined texture to the ground so it is not too boring
	entGround->setMaterialName("Examples/Rockwall");
}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
    int main(int argc, char *argv[])
#endif
    {
        // Create application object
        BasicTutorial2 app;

        try {
            app.go();
        } catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
            std::cerr << "An exception has occured: " <<
                e.getFullDescription().c_str() << std::endl;
#endif
        }

        return 0;
    }

#ifdef __cplusplus
}
#endif
